package com.lch.base.http;


import java.util.List;
import java.util.Map;

import io.reactivex.Observable;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import retrofit2.Response;
import retrofit2.http.Body;
import retrofit2.http.DELETE;
import retrofit2.http.FieldMap;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.GET;
import retrofit2.http.Header;
import retrofit2.http.HeaderMap;
import retrofit2.http.Multipart;
import retrofit2.http.POST;
import retrofit2.http.PUT;
import retrofit2.http.Part;
import retrofit2.http.PartMap;
import retrofit2.http.QueryMap;
import retrofit2.http.Streaming;
import retrofit2.http.Url;

/**
 * 1、Map用来组合复杂的参数，并且对于FieldMap，HeaderMap，PartMap，QueryMap这四种作用方法的注解，其参数类型必须为Map实例，且key的类型必须为String类型，否则抛出异常。
 *   QueryMap作用于Get方法，形成url后面的参数键值对如：name=张三&name=李四&name=王五。同时进行url编码，把中文和特殊字符进行编码
 * 2、Query、QueryMap与Field、FieldMap功能一样，生成的数据形式一样；Query、QueryMap的数据体现在Url上；Field、FieldMap的数据是请求体
 * 3、@FormUrlEncoded 注解和@Multipart 注解不能同时使用，否则会抛出methodError(“Only one encoding annotation is allowed.”)，可在ServiceMethod类中parseMethodAnnotation()方法中找到不能同时使用的具体原因。
 * 4、使用@Body 注解的参数不能使用form 或multi-part编码，即如果为方法使用了FormUrlEncoded或Multipart注解，则方法的参数中不能使用@Body 注解，否则会抛出异常parameterError(p, “@Body parameters cannot be used with form or multi-part encoding.”)
 * 参考：
 * [你真的会用Retrofit2吗?Retrofit2完全教程](http://www.jianshu.com/p/308f3c54abdd)
 * [Retrofit解析2之使用简介](http://www.jianshu.com/p/345304325511)
 * [你应该知道的HTTP基础知识](https://www.jianshu.com/p/e544b7a76dac)
 * 整个请求头的思维导图:https://upload-images.jianshu.io/upload_images/1724103-ea242640383ed739.png
 */
public interface ApiService {

    /**
     * @return Response是retrofit2的， ResponseBody是okhttp3 参考http://www.jianshu.com/p/097947afddaf 这个可以做学习点
     */
    @GET()
    Observable<Response<ResponseBody>> get(
            @Url String url,
            @HeaderMap Map<String, String> headerMap,
            @QueryMap Map<String, Object> reqMap);


    /**
     * 标准普通的post请求
     * FormUrlEncoded:用于修饰Fiedl、FileldMap注解。表示请求正文将使用表单网址编码
     * 使用@FormUrlEncoded 注解的请求将具有"Content-Type: application/x-www-form-urlencoded;charset=utf-8" MIME类型。
     * 字段名称和值将先进行UTF-8进行编码，再根据RFC-3986进行URI编码。
     */
    @FormUrlEncoded
    @POST()
    Observable<Response<ResponseBody>> postForm(
            @Url String url,
            @HeaderMap Map<String, String> headerMap,
            @FieldMap Map<String, Object> params);


    /**
     * post-json格式请求
     *
     * @param jsonBody 可以传所有对象 map object 都可以 retrofit会通过convert把实体序列化
     *                 并自动指定Content-Type: application/json;charset=UTF-8，或者自己添加头部也行@Headers({"Content-Type: application/json","Accept: application/json"})//需要添加头
     *                 还能传RequestBody 作为body，这个就比较麻烦 可以参考：http://www.jianshu.com/p/32bfd5fd8b48
     * @return
     * @Body 注解定义的参数不能为null
     * 当你发送一个post或put请求，但是又不想作为请求参数或表单的方式发送请求时，
     * 使用该注解定义的参数可以直接传入一个实体类，retrofit会通过convert把该实体序列化并将序列化的结果直接作为请求体发送出去。
     */
    @POST
    Observable<Response<ResponseBody>> postJson(
            @Url String url,
            @HeaderMap Map<String, String> headerMap,
            @Body Object jsonBody);

    //--------------上传文件方法---------------------------------------------
    /**
     *
     *  http://www.jianshu.com/p/3f1cc5a7bf8c
     *  为什么可以这样写：
     *  1、 Retrofit会判断@Body的参数类型，如果参数类型为okhttp3.RequestBody,则Retrofit不做包装处理，直接丢给okhttp3处理。而MultipartBody是继承RequestBody，因此Retrofit不会自动包装这个对象。
     *  2、同理，Retrofit会判断@Part的参数类型，如果参数类型为okhttp3.MultipartBody.Part,则Retrofit会把RequestBody封装成MultipartBody，再把Part添加到MultipartBody。

     注意：
     在retrofit中:
     @retrofit2.http.Multipart: 标记一个请求是multipart/form-data类型,需要和@retrofit2.http.POST一同使用，并且方法参数必须是@retrofit2.http.Part注解。
     @retrofit2.http.Part: 代表Multipart里的一项数据,即用${bound}分隔的内容块。
     在okhttp3中:
     okhttp3.MultipartBody :multipart/form-data 的抽象封装,继承okhttp3.RequestBody
     okhttp3.MultipartBody.Part:multipart/form-data里的一项数据。

     */

    /**
     * 通过 MultipartBody和@body作为参数来上传
     * 注意1：必须使用@POST，使用@Body注解参数，则不能使用@Multipart注解方法了
     * 直接将所有的MultipartBody.Part合并到一个MultipartBody中

     * 通过 MultipartBody和@body作为参数来上传
     * @param multipartBody MultipartBody包含多个Part
     */
    @POST()
    Observable<Response<ResponseBody>> postMultipart(
            @Url String url,
            @HeaderMap Map<String, String> headerMap,
            @Body MultipartBody multipartBody);

    //----------以下方法暂时没封装-----------

    /**
     * 上传单个文件的
     * @param url
     * @param headers
     * @param part
     * @return
     */
    @Multipart
    @POST()
    Observable<ResponseBody> uploadFlieWithPart(
            @Url String url,
            @HeaderMap Map<String, String> headers,
            @Part()MultipartBody.Part part);


    /**
     * 通过 List<MultipartBody.Part> 传入多个part实现多文件上传
     * 使用 Multipart : 标记一个请求是Content-Type:multipart/form-data类型,需要和 @retrofit2.http.POST 一同使用，并且方法参数必须是 @retrofit2.http.Part 注解
     * 注意：必须使用@POST，@Multipart注解方法，必须使用@Part
     * @param partList 每个part代表一个
     */
    @Multipart
    @POST()
    Observable<Response<ResponseBody>> uploadFlieWithPartList(
            @Url String url,
            @HeaderMap Map<String, String> headers,
            @Part List<MultipartBody.Part> partList);


    @Multipart
    @POST()
    Observable<Response<ResponseBody>> uploadFlieWithPartMap(
            @Url String fileUrl,
            @HeaderMap Map<String, String> headers,
            @PartMap Map<String, MultipartBody.Part> maps);

    @Multipart
    @POST
    Observable<Response<ResponseBody>> uploadFileWithPartMap(
            @Url() String url,
            @HeaderMap Map<String, String> headers,
            @PartMap() Map<String, RequestBody> partMap,
            @Part() MultipartBody.Part file);

    //----------以上方法暂时没封装-----------


    //----delete  put---------------
    @DELETE()
    Observable<Response<ResponseBody>> delete(
            @Url String url,
            @HeaderMap Map<String, String> headerMap,
            @QueryMap Map<String, Object> maps);

    @PUT()
    Observable<Response<ResponseBody>> put(
            @Url String url,
            @HeaderMap Map<String, String> headerMap,
            @FieldMap Map<String, Object> maps);

    //new---------------------------


    @Multipart
    @POST()
    Observable<Response<ResponseBody>> upLoadImage(
            @Url() String url,
            @Part("image\"; filename=\"image.jpg") RequestBody requestBody);

    @Multipart
    @POST()
    Observable<Response<ResponseBody>> uploadFlie(
            @Url String fileUrl,
            @Part("description") RequestBody description,
            @Part("image\"; filename=\"image.jpg") MultipartBody.Part file);

    @POST()
    Observable<Response<ResponseBody>> uploadFiles(
            @Url() String url,
            @Body Map<String, RequestBody> maps);



    //http://blog.csdn.net/wzgiceman/article/details/52911503
    @Streaming
    @GET
    Observable<ResponseBody> downloadFile(@Url String fileUrl);


    //http://blog.csdn.net/wzgiceman/article/details/52911503
    @Streaming
    @GET
    Observable<ResponseBody> downloadFile(@Header("RANGE") String start, @Url String fileUrl);


    @GET
    Observable<ResponseBody> downloadSmallFile(@Url String fileUrl);


    @POST()
    Observable<ResponseBody> postRequestBody(
            @Url() String url,
            @Body RequestBody Body);
}
